home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 March / EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso / earcd / program / ixemlsrc.lha / ixemul / library / _cli_parse.c < prev    next >
C/C++ Source or Header  |  1995-12-31  |  14KB  |  418 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *
  5.  *  This library is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU Library General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2 of the License, or (at your option) any later version.
  9.  *
  10.  *  This library is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  *  Library General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU Library General Public
  16.  *  License along with this library; if not, write to the Free
  17.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *  _cli_parse.c,v 1.1.1.1 1994/04/04 04:29:41 amiga Exp
  20.  *
  21.  *  _cli_parse.c,v
  22.  * Revision 1.1.1.1  1994/04/04  04:29:41  amiga
  23.  * Initial CVS check in.
  24.  *
  25.  *  Revision 1.3  1992/08/09  20:41:17  amiga
  26.  *  change to use 2.x header files by default
  27.  *
  28.  *  Revision 1.2  1992/07/04  19:09:27  mwild
  29.  *  make stderr (desc 2) *really* read/write, don't just say so...
  30.  *
  31.  * Revision 1.1  1992/05/14  19:55:40  mwild
  32.  * Initial revision
  33.  *
  34.  */
  35.  
  36. /*
  37.  *    This routine is called from the _main() routine and is used to
  38.  *    parse the arguments passed from the CLI to the program. It sets
  39.  *    up an array of pointers to arguments in the global variables and
  40.  *    and sets up _argc and _argv which will be passed by _main() to
  41.  *    the main() procedure. If no arguments are ever going to be
  42.  *    parsed, this routine may be replaced by a stub routine to reduce
  43.  *    program size.
  44.  *
  45.  */
  46.  
  47. #define KERNEL
  48. #include "ixemul.h"
  49. #include "kprintf.h"
  50. #include <stdio.h>
  51. #include <ctype.h>
  52. #include <string.h>
  53. #include <glob.h>
  54.  
  55. #ifndef MAXPATHLEN
  56. #define MAXPATHLEN 1024
  57. #endif
  58.  
  59. extern int __read(), __write(), __ioctl(), __fselect(), __close();
  60.  
  61. /* We will store all arguments in this double linked list, the list
  62.  * is always sorted according to elements, ie. order of given arguments
  63.  * is preserved, but all elements, that get expanded will be sorted
  64.  * alphabetically in this list. */
  65.  
  66. struct ArgList {
  67.   struct MinList al_list;  /* the list - head */
  68.   long         al_num;   /* number of arguments in the whole list */
  69. };
  70.  
  71. struct Argument {
  72.   struct MinNode a_node;   /* the link in the arg-list */
  73.   char        *a_arg;       /* a malloc'd string, the argument */
  74. };
  75.  
  76. /* insert a new argument into the argument vector, we have to keep the
  77.  * vector sorted, but only element-wise, otherwise we would break the
  78.  * order of arguments, and "copy b a" is surely not the same as "copy a b"..
  79.  * so we don't scan the whole list, but start with element "start",
  80.  * if set, else we start at the list head */
  81.  
  82. static void 
  83. AddArgument (struct ArgList *ArgList,
  84.          struct Argument *start, struct Argument *arg, long size)
  85. {
  86.   register struct Argument *el, *nel;
  87.  
  88.   /* depending on "start", start scan for right position in list at
  89.    * successor of start or at head of list  */
  90.   for (el = (struct Argument *)
  91.         (start ? start->a_node.mln_Succ : ArgList->al_list.mlh_Head);
  92.        (nel = (struct Argument *) el->a_node.mln_Succ);
  93.        el = nel)
  94.     if (strcmp (el->a_arg, arg->a_arg) > 0) break;
  95.  
  96.   Insert ((struct List *) ArgList, (struct Node *) arg, (struct Node *) el);
  97.  
  98.   /* and bump up the argument counter once */
  99.   ++ ArgList->al_num;
  100. }
  101.  
  102. /* if an argument contains one or more of these characters, we have to
  103.  * call the glob() stuff, else don't bother expanding and
  104.  * quickly append to list. Here's the meaning of all these characters, some
  105.  * seem to be not widely used:
  106.  * *    match any number (incl. zero) of characters
  107.  * #?    match any number (incl. zero) of characters (for Amiga compatibility)
  108.  * []   match any character that's contained in the set inside the brackets
  109.  * ?    match any character (exactly one)
  110.  * !    negate the following expression
  111.  */
  112.  
  113. #define iswild(ch) (index ("*[!?#", ch) ? 1 : 0)
  114.  
  115. void
  116. _cli_parse(struct Process *this_proc, long alen, char *_aptr,
  117.        int *argc, char ***argv)
  118. {
  119.   char *arg0;
  120.   struct CommandLineInterface *cli;
  121.   struct Argument *arg, *narg;
  122.   char *line, *next, *lmax, **cpp;
  123.   int do_expand;
  124.   int arglen;
  125.   char *aptr;
  126.   struct ArgList ArgList;
  127.   int expand_cmd_line = u.u_expand_cmd_line;
  128.   struct file *fin, *fout;
  129.   int fd;  
  130.   BPTR fh;
  131.   int omask;
  132.  
  133.   KPRINTF (("entered _cli_parse()\n"));
  134.   KPRINTF (("command line length = %ld\n", alen));
  135.   KPRINTF (("command line = '%s'\n", _aptr));
  136.  
  137.   /* this stuff has been in ix_open before, but it really belongs here, since
  138.    * I don't want it to happen by default on OpenLibrary, since it would
  139.    * disturb any vfork() that wants to inherit files from its parent 
  140.    */
  141.  
  142.   omask = syscall (SYS_sigsetmask, ~0);
  143.  
  144.   if (! falloc (&fin, &fd))
  145.     {
  146.       /*
  147.        * NOTE: if there's an error creating one of the standard
  148.        *       descriptors, we just go on, the descriptor in
  149.        *       question will then not be set up, no problem ;-)
  150.        */
  151.       if (fd != 0)
  152.     ix_panic ("allocated stdin is not fd #0!");
  153.                
  154.       if (! falloc (&fout, &fd))
  155.     {
  156.       if (fd != 1)
  157.         ix_panic ("allocated stdout is not fd #1!");
  158.  
  159.       if ((fh = Input ()))
  160.         {
  161.           fin->f_name = "<Standard Input>";
  162.           fin->f_fh   = (struct FileHandle *)BTOCPTR(fh);
  163.           __init_std_packet(&fin->f_sp);
  164.           __fstat(fin);
  165.           fin->f_flags = FREAD|FEXTOPEN|FTTYCRNL;
  166.           fin->f_type  = DTYPE_FILE;
  167.           fin->f_read  = __read;
  168.           fin->f_write = 0;
  169.           fin->f_ioctl = __ioctl;
  170.           fin->f_close = __close;
  171.           fin->f_select= __fselect;
  172.         }
  173.       else
  174.         {
  175.           u.u_ofile[0] = 0;
  176.           fin->f_count--;
  177.         }
  178.                     
  179.       if ((fh = Output ()))
  180.         {
  181.           fout->f_name = "<Standard Output>";
  182.           fout->f_fh   = (struct FileHandle *)BTOCPTR(fh);
  183.           __init_std_packet(&fout->f_sp);
  184.           __fstat(fout);
  185.           fout->f_flags = FWRITE|FEXTOPEN;
  186.           fout->f_type  = DTYPE_FILE;
  187.           fout->f_read  = 0;
  188.           fout->f_write = __write;
  189.           fout->f_ioctl = __ioctl;
  190.           fout->f_close = __close;
  191.           fout->f_select= __fselect;
  192.         }
  193.       else
  194.         {
  195.           u.u_ofile[1] = 0;
  196.           fout->f_count--;
  197.         }
  198.  
  199.       /* deal with stderr. Seems this was a last minute addition to 
  200.          dos 2, it's hardly documented, there are no access functions,
  201.          nobody seems to know what to do with pr_CES...
  202.          If pr_CES is valid, then we use it, otherwise we open the
  203.          console. */
  204.  
  205.       fd = -1;
  206.       if ((fh = this_proc->pr_CES))
  207.         {
  208.           struct file *fp;
  209.  
  210.           if (!falloc (&fp, &fd))
  211.             {
  212.           fp->f_name = "<Standard Error>";
  213.           fp->f_fh   = (struct FileHandle *)BTOCPTR(fh);
  214.           __init_std_packet(&fp->f_sp);
  215.           __fstat(fp);
  216.           fp->f_flags = FREAD|FWRITE|FEXTOPEN;
  217.           fp->f_type  = DTYPE_FILE;
  218.           fp->f_read  = __read;
  219.           fp->f_write = __write;
  220.           fp->f_ioctl = __ioctl;
  221.           fp->f_close = __close;
  222.           fp->f_select= __fselect;
  223.             }
  224.         }
  225.         /* Apparently use of CONSOLE: gave problems with
  226.                Emacs, so we continue to use "*" instead. */
  227.  
  228.             /* Here is some more information on this from Joerg Hoehle:
  229.              *
  230.          * While writing fifolib38_1 I found that console handlers are sent
  231.          * ACTION_FIND* packets with names of either "*" or "Console:", depending
  232.          * on what the user typed. Old handlers that do not recognize "CONSOLE:"
  233.          * will produce strange results which could explain the above problems.
  234.              *
  235.          * That's the reason why
  236.          *     echo foo >*    (beware of * expansion in a non-AmigaDOS shell)
  237.          * works in an Emacs shell buffer, whereas
  238.          *     echo foo >console:
  239.          * won't with fifolib prior to version 38.1.
  240.              *
  241.          * I believe that programs opening stderr should continue to open "*" for
  242.          * compatibility reasons. Opening "CONSOLE:" first and "*" if it fails is
  243.          * _not_ a solution: for example, FIFO: (prior to 38.1) accepts the
  244.          * Open("CONSOLE:") call, giving a FIFO that can be neither read nor
  245.          * written to :-(
  246.          */
  247.       if (fd == -1)
  248.         fd = syscall(SYS_open, "*", 2);
  249.       if (fd > -1 && fd != 2)
  250.         {
  251.           syscall(SYS_dup2, fd, 2);
  252.           syscall(SYS_close, fd);
  253.         }
  254.  
  255.     } /* falloc (&fout, &fd) */
  256.     } /* falloc (&fin, &fd) */
  257.  
  258.   aptr = alloca (alen + 1);
  259.   memcpy(aptr, _aptr, alen + 1);
  260.  
  261.   cli = (struct CommandLineInterface *) BTOCPTR (this_proc->pr_CLI);
  262.   arg0 = (char *) BTOCPTR (cli->cli_CommandName);
  263.  
  264.   /* init our argument list */
  265.   NewList ((struct List *) &ArgList);
  266.  
  267.   /* lets start humble.. no arguments at all:-)) */
  268.   ArgList.al_num = 0;
  269.  
  270.   /* find end of command-line, stupid BCPL-stuff.. line can end
  271.    * either with \n or with \0 .. */
  272.   for (lmax = aptr; *lmax && *lmax != '\n' && *lmax != '\r'; ++lmax) ;
  273.   *lmax = 0;
  274.  
  275.   /* loop over all arguments, expand all */
  276.   for (line = aptr, narg = arg = 0; line < lmax; )
  277.     {
  278.       do_expand = 0;
  279.  
  280.       KPRINTF (("remaining cmd line = '%s'\n", aptr));
  281.       /* skip over leading whitespace */
  282.       while (isspace (*line)) ++line;
  283.       if (line >= lmax)
  284.     break;
  285.  
  286.       /* if argument starts with ", don't expand it and remove the " */
  287.       if (*line == '\"')
  288.     {
  289.       KPRINTF (("begin quoted argument at '%s'\n", line));
  290.       /* scan for end of quoted argument, this can be either at
  291.        * end of argumentline or at a second " */
  292.       line++;
  293.       next = line;
  294.       while (next < lmax && *next != '\"')
  295.         {
  296.           /* Prevent that the loop terminates due to an escaped quote.
  297.            * However, if the character after the quote is a space, then
  298.            * it is ambiguous whether or not the quote is escaped or is
  299.            * the end of the argument.  Consider what happens when you give
  300.            * /bin/sh a 'FS=\' argument.  This gets passed to ixemul.library
  301.            * as "FS=\" <other args> */
  302.           if ((*next == '\'' || *next == '\\') && next[1] == '\"')
  303.         {
  304.           /* in this case we have to shift the whole remaining
  305.            * line one position to the left to skip the 
  306.            * escape-character */
  307.           bcopy (next + 1, next, (lmax - next) + 1);
  308.           --lmax;
  309.         }
  310.  
  311.           ++next;
  312.         }
  313.       *next = 0;
  314.       KPRINTF (("got arg '%s'\n", line));
  315.     }
  316.       else
  317.     {
  318.           /* strange kind of BCPL-quoting, if you want to get a " thru,
  319.            * you have to quote it with a ', eq. HELLO'"WORLD'" will preserve
  320.            * the " inside the argument. Since hardly anyone knows this
  321.            * "feature", I allow for the more common Unix-like escaping, ie
  322.            * \" will give you the same effect as '". */
  323.           if ((*line == '\'' || *line == '\\') && line[1] == '\"')
  324.             {
  325.             KPRINTF (("found escaped quote at '%s'\n", line));
  326.             line++;
  327.           }
  328.       /* plain, vanilla argument.. */
  329.       next = line + 1;
  330.       /* check, whether we have to run thru the expander, or
  331.        * if we rather can just copy over the whole argument */
  332.       do_expand = iswild (*line);
  333.       /* skip over element and make it 0-terminated .. */
  334.       while (next < lmax && !isspace (*next))
  335.         {
  336.           do_expand |= iswild (*next);
  337.           if ((*next == '\'' || *next == '\\') && next[1] == '\"')
  338.         {
  339.           bcopy (next + 1, next, (lmax - next) + 1);
  340.           --lmax;
  341.         }
  342.  
  343.           ++next;
  344.         }
  345.       *next = 0;
  346.     }
  347.  
  348.       if (expand_cmd_line && do_expand)
  349.     {
  350.           glob_t g;
  351.           char **p;
  352.           
  353.           syscall (SYS_sigsetmask, omask);
  354.           syscall (SYS_glob, line,
  355.                    ((ix.ix_flags & ix_unix_pattern_matching_case_sensitive) ? 0 : GLOB_NOCASE) |
  356.                    ((ix.ix_flags & ix_allow_amiga_wildcard) ? GLOB_AMIGA : 0) |
  357.                GLOB_NOCHECK, NULL, &g);
  358.           omask = syscall (SYS_sigsetmask, ~0);
  359.           for (p = g.gl_pathv; *p; p++)
  360.             {
  361.               arg = (struct Argument *)syscall(SYS_malloc, sizeof(*arg));
  362.               arg->a_arg = *p;
  363.               AddArgument(&ArgList, narg, arg, strlen(*p));
  364.               narg = (struct Argument *) ArgList.al_list.mlh_TailPred;
  365.             }
  366.           syscall(SYS_free, g.gl_pathv);
  367.     }
  368.       else  /* ! do_expand */
  369.     {
  370.       /* just add the argument "as is" */
  371.       arg = (struct Argument *) syscall (SYS_malloc, sizeof (*arg));
  372.       arglen = strlen (line);
  373.       arg->a_arg = (char *) syscall (SYS_malloc, arglen+1);
  374.       strcpy (arg->a_arg, line);
  375.       AddArgument (&ArgList, narg, arg, arglen);
  376.     }
  377.  
  378.       narg = (struct Argument *) ArgList.al_list.mlh_TailPred;
  379.       line = next + 1;
  380.     } /* for */
  381.  
  382.   /* prepend the program name */
  383.   arg = (struct Argument *) syscall (SYS_malloc, sizeof (*arg));
  384.  
  385.   /* some stupid shells (like Wsh...) pass the WHOLE path of the
  386.    * started program. We simply cut off what we don't want ;-)) */
  387.   for (arglen = arg0[0]; arglen; --arglen)
  388.     if (arg0[arglen] == ':' || arg0[arglen] == '/')
  389.       break;
  390.  
  391.   line = &arg0[arglen+1];
  392.   arglen = arg0[0] - arglen;
  393.  
  394.   arg->a_arg = (char *) syscall (SYS_malloc, arglen + 1);
  395.  
  396.   strncpy (arg->a_arg, line, arglen);
  397.   arg->a_arg[arglen] = 0;
  398.   /* did I tell you, that I like those kernel list calls ?? */
  399.   AddHead ((struct List*) &ArgList, (struct Node*) arg);
  400.   ++ ArgList.al_num;
  401.  
  402.   /* build _argv array */
  403.   *argv = (char **) syscall (SYS_malloc, (ArgList.al_num+1) * sizeof(char *));
  404.   for (cpp = *argv, arg = (struct Argument *) ArgList.al_list.mlh_Head;
  405.        (narg = (struct Argument *) arg->a_node.mln_Succ);
  406.        arg = narg)
  407.     *cpp++ = arg->a_arg;
  408.  
  409.   /* guarantee last element == 0 */        
  410.   *cpp = 0;
  411.   KPRINTF_ARGV ("argv", *argv);
  412.   *argc = ArgList.al_num;
  413.   KPRINTF (("argc = %ld\n", *argc));
  414.   KPRINTF (("leaving _cli_parse()\n"));
  415.  
  416.   syscall (SYS_sigsetmask, omask);
  417. }
  418.